package pacman.entries.pacman.ml.features.unittests;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Random;

import org.junit.Test;

import pacman.game.Constants.GHOST;
import pacman.game.Game;
import pacman.game.internal.Ghost;
import pacman.ml.features.*;

public class FeatureUnitTest {

	@Test
	public void EdibleGhostsTest() {
		
		FeatureExtractor fe = FeatureExtractor.getInstance(EdibleGhostsFeature.class, 0);
		
		long cases = 4;
		Random rnd=new Random(0);

		Game game = new Game(rnd.nextLong());
		
		for(int i = 0; i<=cases; i++) {
			int j = 0;
			for(GHOST ghost : game.ghosts.keySet()) {
				if(j++ >= i)
					game.ghosts.get(ghost).edibleTime = 0;
				else
					game.ghosts.get(ghost).edibleTime = 1;
				
			}
			float value = fe.GetValue(game);
			// assertEquals(value, (float)i);
			if(value != (i>0?1:0)) {
				fail(i+"edible ghosts.  "+value+" extracted");
			}
			game.updateGame();
		}
	}
	
	@SuppressWarnings("unused")
	private int[] getNodesOfShortestPath(int index) {
		int larger = (int)((-1.0 + Math.sqrt(1 + 8*index))/2);
		int smaller = index - larger*(larger+1)/2;
		int[] nodes = {larger,smaller};
		return nodes;
	}
	
	// returns the shortest path index, not the shortest path
	private int getShortestPathFromNodes(int start, int end) {
		int large = Math.max(start,end);
		int small = Math.min(start, end);
		return ((large*(large+1))/2)+small;
	}
	
	private class Triple {
		public Triple(int a, int b, int c) {
			this.a=a;
			this.b=b;
			this.c=c;
		}
		public int a;
		public int b;
		@SuppressWarnings("unused")
		public int c;
	}
	
	@Test
	public void NonEdibleGhostDistanceThresholdTest() {
		int pathMin = 20;
		FeatureExtractor fe = FeatureExtractor.getInstance(NonEdibleGhostDistanceThresholdFeature.class, pathMin);
		
		long cases = 100;
		Random rnd=new Random(0);

		Game game = new Game(rnd.nextLong());
		// int totalDistances = game.currentMaze.shortestPathDistances.length;
		int totalNodes = game.currentMaze.graph.length;
		ArrayList<ArrayList<Triple>> shortDistances = new ArrayList<ArrayList<Triple>>(totalNodes);
		ArrayList<ArrayList<Triple>> longDistances = new ArrayList<ArrayList<Triple>>(totalNodes);
		
		for(int start = 0; start < totalNodes; start++) {
			ArrayList<Triple> shortDistancesFromStart = new ArrayList<Triple>(start);
			ArrayList<Triple> longDistancesFromStart = new ArrayList<Triple>(start);
			for(int end = 0; end <= start; end++ ){
				int dist = game.currentMaze.shortestPathDistances[getShortestPathFromNodes(start, end)];
				Triple tr = new Triple(start,end,dist);
				if(dist < pathMin) {
					shortDistancesFromStart.add(tr);
				}
				else {
					longDistancesFromStart.add(tr);
				}
			}
			shortDistances.add(shortDistancesFromStart);
			longDistances.add(longDistancesFromStart);
		}
		
		boolean closeGhost = false;
		for(int i = 0; i<=cases; i++) {
			
			int pacmanNode = rnd.nextInt(totalNodes);
			ArrayList<Triple> shortDistancesFromStart = shortDistances.get(pacmanNode);
			ArrayList<Triple> longDistancesFromStart = longDistances.get(pacmanNode);
			
			game.pacman.currentNodeIndex = pacmanNode;
			
			if(closeGhost) {
				int numCloseGhosts = 1+rnd.nextInt(4);   // 1 to 4 close ghosts
				int j = 0;
				for(GHOST ghost : game.ghosts.keySet()) {
					Ghost g = game.ghosts.get(ghost);
					if(j++ >= numCloseGhosts) {
						Triple loc = longDistancesFromStart.get(rnd.nextInt(longDistancesFromStart.size()));
						int ghostNode = loc.a;
						if(loc.a == pacmanNode)
							ghostNode = loc.b;
						g.currentNodeIndex = ghostNode;
					}
					else {
						Triple loc = shortDistancesFromStart.get(rnd.nextInt(shortDistancesFromStart.size()));
						int ghostNode = loc.a;
						if(loc.a == pacmanNode)
							ghostNode = loc.b;
						g.currentNodeIndex = ghostNode;
					}
					g.lairTime = 0;
				}
			}
			else {
				for(GHOST ghost : game.ghosts.keySet()) {
					Triple loc = longDistancesFromStart.get(rnd.nextInt(longDistancesFromStart.size()));
					int ghostNode = loc.a;
					if(loc.a == pacmanNode)
						ghostNode = loc.b;
					Ghost g = game.ghosts.get(ghost);
					g.currentNodeIndex = ghostNode;
					g.lairTime = 0;
				}
			}
			
			float value = fe.GetValue(game);
			// assertEquals(value, (float)i);
			if(value != (closeGhost?1:0)) {
				fail("Ghosts are closer than "+pathMin+"? "+closeGhost+".  Value = "+value);
			}
			game.updateGame();
			closeGhost = !closeGhost;
		}
		
	}

}
